package sandbox

import (
	"chainmaker/sdk"
	"errors"
	"fmt"

	"chainmaker/pb/protogo"
)

//NewProxyContract create a union contract instance
func NewProxyContract(contractNames []string, contracts []sdk.Contract) (*ProxyContract, error) {
	if len(contracts) != len(contractNames) {
		return nil, errors.New("contract count not equal contract name count")
	}

	result := &ProxyContract{contractMap: map[string]sdk.Contract{}}
	for index := range contracts {
		result.contractMap[contractNames[index]] = contracts[index]
	}
	return result, nil
}

// union contract
type ProxyContract struct {
	contractMap map[string]sdk.Contract
}

func (uc *ProxyContract) InitContract() protogo.Response {
	contractName := sdk.Instance.(*sdk.SDK).GetContractName()
	sdk.Instance.Debugf("init contract start. name:%s ", contractName)
	cas, ok := uc.contractMap[sdk.Instance.(*sdk.SDK).GetContractName()]
	if !ok {
		return Error("invoke contract failed. contract not found. name: " + contractName)
	}

	response := cas.InitContract()

	sdk.Instance.Debugf("init contract end. depth:%d, name:%s, response:{status:%d, message:%s, payload:%s}",
		sdk.Instance.(*sdk.SDK).GetDepth(), contractName,
		response.Status, response.Message, string(response.Payload))
	return response
}

func (uc *ProxyContract) UpgradeContract() protogo.Response {
	contractName := sdk.Instance.(*sdk.SDK).GetContractName()
	sdk.Instance.Debugf("upgrade contract start. name:%s ", contractName)
	cas, ok := uc.contractMap[sdk.Instance.(*sdk.SDK).GetContractName()]
	if !ok {
		return Error("invoke contract failed. contract not found. name: " + contractName)
	}

	response := cas.InitContract()

	sdk.Instance.Infof("upgrade contract end. depth:%d, name:%s, response:{status:%d, message:%s, payload:%s}",
		sdk.Instance.(*sdk.SDK).GetDepth(), contractName,
		response.Status, response.Message, string(response.Payload))
	return response
}

func (uc *ProxyContract) InvokeContract(method string) protogo.Response {
	depth := sdk.Instance.(*sdk.SDK).GetDepth()
	contractName := sdk.Instance.(*sdk.SDK).GetContractName()
	sdk.Instance.Debugf("invoke contract start. depth:%d, name:%s, method:%s",
		depth, contractName, method)

	if depth > 5 {
		message := fmt.Sprintf("call contract reach depth limit. [depth:%d > max depth %d]",
			depth, sdk.MaxDepth)
		sdk.Instance.Warnf(message)
		return Error(message)
	}

	cas, ok := uc.contractMap[sdk.Instance.(*sdk.SDK).GetContractName()]
	if !ok {
		return Error("invoke contract failed. contract not found. name: " + contractName)
	}

	response := cas.InvokeContract(method)

	sdk.Instance.Debugf("invoke contract end. depth:%d, name:%s, method:%s, response:{status:%d, message:%s, result:%s}",
		sdk.Instance.(*sdk.SDK).GetDepth(), contractName, method,
		response.Status, response.Message, string(response.Payload))
	return response
}
